#include "stdafx.h"
#include "win_process.h"

_SSL_BEGIN
_SSWINOSL_BEGIN

win_proces::win_proces()
{
	stdin_write_h_ = NULL;
	stdout_read_h_ = NULL;
	stderr_read_h_ = NULL;
	process_ = NULL;
}

win_proces::~win_proces()
{
	SS_ASSERT(!is_runing());
	destroy();
}

bool win_proces::create(const tstring &command_line, unsigned short show_mode /*= SW_SHOW*/,
	const tstring &work_directory /*= NULL*/, bool use_std_handle /*= false*/,
	DWORD create_flags /*= 0*/, const tstring &desktop /*= NULL*/)
{
	bool ret = false;

	tchar command_line_copy[MAX_PATH * 2];

	HANDLE stdin_r = NULL, stdin_w = NULL;
	HANDLE stdout_r = NULL, stdout_w = NULL;
	HANDLE stderr_r = NULL, stderr_w = NULL;
	PROCESS_INFORMATION pi = {0};
	if(process_ != NULL)
	{
		if(is_runing())
			return false;
		destroy();
	}
	if(command_line.empty())
		return false;
	_tcscpy_s(command_line_copy, command_line.c_str());

	STARTUPINFO si = { sizeof(si) };
	GetStartupInfo(&si);
	si.dwFlags = STARTF_USESHOWWINDOW;
	si.wShowWindow = show_mode;
	
	tchar desktop_copy[MAX_PATH] = {0};
	if(!desktop.empty())
	{
		_tcscpy_s(desktop_copy, desktop.c_str());
		si.lpDesktop = desktop_copy;
	}

	if(use_std_handle)
	{
		SECURITY_ATTRIBUTES sa = { sizeof(sa) };
		sa.bInheritHandle = TRUE;
		if(!CreatePipe(&stdin_r, &stdin_w, &sa, 0))
			goto finish;
		SetHandleInformation(stdin_w, HANDLE_FLAG_INHERIT, 0);

		if(!CreatePipe(&stdout_r, &stdout_w, &sa, 0))
			goto finish;
		SetHandleInformation(stdout_r, HANDLE_FLAG_INHERIT, 0);

		if(!CreatePipe(&stderr_r, &stderr_w, &sa, 0))
			goto finish;
		SetHandleInformation(stderr_r, HANDLE_FLAG_INHERIT, 0);

		si.hStdInput = stdin_r;
		si.hStdOutput = stdout_w;
		si.hStdError = stderr_w;
		si.dwFlags |= STARTF_USESTDHANDLES;
	}

	if(!CreateProcess(NULL, command_line_copy, NULL, NULL, TRUE, create_flags, NULL,
		work_directory.empty() ? NULL : work_directory.c_str(), &si, &pi))
		goto finish;

	process_ = pi.hProcess;
	pi.hProcess = NULL;

	if(use_std_handle)
	{
		stdin_write_h_ = stdin_w;
		stdin_w = NULL;
		stdout_read_h_ = stdout_r;
		stdout_r = NULL;
		stderr_read_h_ = stderr_r;
		stderr_r = NULL;
	}

	ret = true;
finish:

	if(pi.hProcess != NULL)
	{
		CloseHandle(pi.hProcess);
		pi.hProcess = NULL;
	}
	if(pi.hThread != NULL)
	{
		CloseHandle(pi.hThread);
		pi.hThread = NULL;
	}

	if(stderr_w != NULL)
	{
		CloseHandle(stderr_w);
		stderr_w = NULL;
	}

	if(stderr_r != NULL)
	{
		CloseHandle(stderr_r);
		stderr_r = NULL;
	}
	if(stdout_w != NULL)
	{
		CloseHandle(stdout_w);
		stdout_w = NULL;
	}

	if(stdout_r != NULL)
	{
		CloseHandle(stdout_r);
		stdout_r = NULL;
	}

	if(stdin_w != NULL)
	{
		CloseHandle(stdin_w);
		stdin_w = NULL;
	}

	if(stdin_r != NULL)
	{
		CloseHandle(stdin_r);
		stdin_r = NULL;
	}
	return ret;
}

void win_proces::destroy()
{
	if(is_runing())
	{
		TerminateProcess(process_, ~0);
	}

	if(process_ != NULL)
	{
		CloseHandle(process_);
		process_ = NULL;
	}

	if(stdin_write_h_ != NULL)
	{
		CloseHandle(stdin_write_h_);
		stdin_write_h_ = NULL;
	}
	if(stdout_read_h_ != NULL)
	{
		CloseHandle(stdout_read_h_);
		stdout_read_h_ = NULL;
	}
	if(stderr_read_h_ != NULL)
	{
		CloseHandle(stderr_read_h_);
		stderr_read_h_ = NULL;
	}
}

bool win_proces::join(unsigned long milli_seconds /*= INFINITE*/, unsigned long *exit_code /*= NULL*/)
{
	if(process_ == NULL)
	{
		TraceVE(_T("The Handle of process is empty!"));
		return false;
	}

	TraceVT(_T("Before Wait process 0x%x"), milli_seconds);
	DWORD ret = WaitForSingleObject(process_, milli_seconds);
	TraceVT(_T("After Wait process"));
	if(ret != WAIT_OBJECT_0)
		return false;

	DWORD exit_code_l = 0;
	if(!GetExitCodeProcess(process_, &exit_code_l))
	{
		TraceVW(_T("GetExitCodeProcess failed!"));
		exit_code_l = ~0;
	}
	if(exit_code != NULL)
		*exit_code = exit_code_l;

	CloseHandle(process_);
	process_ = NULL;

	destroy();

	return true;
}

bool win_proces::detach(
	HANDLE *process_h /* = NULL */,
	HANDLE *stdin_h /* = NULL */,
	HANDLE *stdout_h /* = NULL */,
	HANDLE *stderr_h /* = NULL */)
{
	if(process_ == NULL)
		return false;

	if(process_h != NULL)
	{
		*process_h = process_;
		process_ = NULL;
	}

	if(stdin_h != NULL)
	{
		*stdin_h = stdin_write_h_;
		stdin_write_h_ = NULL;
	}

	if(stdout_h != NULL)
	{
		*stdout_h = stdout_read_h_;
		stdout_read_h_ = NULL;
	}

	if(stderr_h != NULL)
	{
		*stderr_h = stderr_read_h_;
		stderr_read_h_ = NULL;
	}

	destroy();
	return true;
}

bool win_proces::wait(unsigned long milli_seconds /*= INFINITE*/)
{
	return WaitForSingleObject(process_, milli_seconds) != WAIT_TIMEOUT;
}

bool win_proces::get_exitcode(unsigned long *exit_code)
{
	if(process_ == NULL || exit_code == NULL)
		return false;
	return ::GetExitCodeProcess(process_, exit_code) ? true : false;
}

bool win_proces::write(const void* buffer, unsigned long buffer_bytes, unsigned long *trans_bytes)
{
	if(stdin_write_h_ == NULL)
		return FALSE;
	return WriteFile(stdin_write_h_, buffer, buffer_bytes, trans_bytes, NULL) ? true : false;
}

bool win_proces::read(void* buffer, unsigned long bytes_to_read, unsigned long *trans_bytes,
	unsigned long milli_seconds /*= INFINITE*/, bool use_std_eror_handle /*= false*/)
{

	HANDLE std_read_h = use_std_eror_handle ? stderr_read_h_ : stdout_read_h_;
	if(std_read_h == NULL)
		return false;

	if(milli_seconds != INFINITE)
	{
		DWORD total_bytes_avail = 0;
		DWORD tick_count = ::GetTickCount();
		do
		{
			total_bytes_avail = 0;
			if(!PeekNamedPipe(std_read_h, NULL, 0, NULL, &total_bytes_avail, NULL))
				return false;
			if(total_bytes_avail == 0)
			{
				Sleep(100);
			}
		} while(total_bytes_avail == 0 && ::GetTickCount() - tick_count < milli_seconds);


		if(total_bytes_avail == 0)
		{
			if(trans_bytes != NULL)
				*trans_bytes = 0;
			return true;
		}
	}
	return ReadFile(std_read_h, buffer, bytes_to_read, trans_bytes, NULL) ? true : false;
}

HANDLE win_proces::get_process_handle() const
{
	return process_;
}

HANDLE win_proces::get_stderror_handle() const
{
	return stderr_read_h_;
}

HANDLE win_proces::get_stdout_handle() const
{
	return stdout_read_h_;
}

bool win_proces::is_runing()
{
	if(process_ == NULL)
		return false;

	return WaitForSingleObject(process_, 0) == WAIT_TIMEOUT;
}

_SSWINOSL_END
_SSL_END

